package Population;

import java.util.Random;

import Groups.*;
import World.*;

public class Population {
	public static Inhabitant[] popArray;
	public static Group[] groups;
	int test = 0;
	double year = 0;
	int yearStepCounter = 0;
	int stepsPerYear = 12;
	int chanceOfDeath = 15000000;
	int numDie = 0;
	int numBorn = 0;
	
		public  int totalBorn = 0;
		public  int totalDied = 0;
		public  double averageLifeExpectancy = -1;
		public  int youngestDeath = 99999, youngID=-1;
		public  int oldestDeath = 0, oldID = -1;

	public Population(int popSize)
	{
		groups = new Group[1];
		popArray = new Inhabitant[popSize];
		for (int i=0; i<popSize; i++)
			popArray[i] = new Person(-1,-1,-1,-2);

		groups[0] = new Wanderer(popArray);
	}
		
	public Population(int genLimit, int popSize)
	{
		popArray = new Inhabitant[popSize];
		groups = new Group[0];
		GenerateInhabitants(genLimit, popSize);
		ReallocateGroupArray(1);
		groups[0] = new Wanderer(popArray);
	}
	
	public void ReallocatepopArray(int size)
	{
		Inhabitant[] tempInhab = new Inhabitant[size];
		if(popArray.length < size)
		{
			for (int i =0; i<popArray.length; i++)
			{
				tempInhab[i] = popArray[i];
			}
			popArray = new Inhabitant[size];
			for (int i =0; i<popArray.length; i++)
			{
				popArray[i] = tempInhab[i];
			}
		}
		else if ( size < popArray.length)
		{
			int count = 0;
			for (int i =0; i<popArray.length; i++)
			{
				if(popArray[i].isAlive)
				{
					tempInhab[count] = popArray[i];
					count++;
				}
				if(count == size)
					break;
			}
			popArray = new Inhabitant[size];
			for (int i =0; i<popArray.length; i++)
			{
				popArray[i] = tempInhab[i];
			}
		}
	}
	
	
	public void ReallocateGroupArray(int size)
	{
		Group[] tempGroups = new Group[size];
		if(groups.length < size)
		{
			for (int i =0; i<groups.length; i++)
			{
				tempGroups[i] = groups[i];
			}
			groups = new Group[size];
			for (int i =0; i<groups.length; i++)
			{
				groups[i] = tempGroups[i];
			}
		}
		else if ( size < groups.length)
		{
			int count = 0;
			for (int i =0; i<groups.length; i++)
			{
				if(groups[i].toBeRemoved)
				{
					tempGroups[count] = groups[i];
					count++;
				}
				if(count == size)
					break;
			}
			groups = new Group[size];
			for (int i =0; i<popArray.length; i++)
			{
				groups[i] = tempGroups[i];
			}
		}
	}
	
	/**
	 * Generates the inhabitants of the world.
	 * @param width
	 * @param height
	 * @param genLimit
	 * @param totalPop
	 */
	public void GenerateInhabitants(int genLimit, int totalPop)
	{
		Random generator = new Random();
		int count = 0, num;
		boolean complete = false;
		int width = World.environGrid[0].length;
		int height = World.environGrid.length;
		
		for (int i=0; i<totalPop; i++)
			popArray[i] = new Person(-1,-1,-1,-2);
		
		while (!complete)
		{
			for(int i=0; i<width; i++)
			{
				for(int j=0; j<height; j++)
				{
					// generate the popArray
					num = generator.nextInt(genLimit);
					if(num == 1 && World.environGrid[j][i].isHabitable && FindInhabitant(i, j).getX() == -1)
					{
						popArray[count] = new Person(i,j,GetAvailableID(),-1);
						count++;
					}			
					if (count >= totalPop)
					{
						i=width;
						j=height;
					}
				}
			}
			if(count >= totalPop)
			{
				complete=true;
			}
		}
	}
	
	/**
	 * Run the actions of each little bugger.
	 */
	public void Update()
	{ //TODO: Add more
		
		numBorn = 0;
		/* Run the current AI */
		
		yearStepCounter++;
		/*for (int i=0; i<popArray.length; i++)
		{
			popArray[i].AI();
		}*/
		for(int i=0; i<groups.length; i++)
			groups[i].Update();
		
		/* Run life functions */
		BringOutYourDead();
		if (yearStepCounter == stepsPerYear)
		{
			AgePopulation();
			yearStepCounter=0;
			year++;
		}
		
		/* Tests */

	}

	public double getYear()
	{
		return year;
	}
	
	public int getMonth()
	{
		return yearStepCounter;
	}
	
	/**
	 * checks for an inhabitant at position x and y.
	 * @param posX
	 * @param posY
	 * @return inhabitant or null if not found
	 */
	public Inhabitant FindInhabitant(int posX, int posY)
	{
		for(int i=0; i<popArray.length; i++)
		{
			if(popArray[i].getX()==posX && popArray[i].getY()==posY)
			{
				return popArray[i];
			}
		}
		return new Null_Inhab();
	}
	
	public static boolean InhabitantCollision(int posX, int posY)
	{
		for(int i=0; i<popArray.length; i++)
		{
			if(popArray[i].getX()==posX && popArray[i].getY()==posY)
			{
				return true;
			}
		}
		return false;
	}
	
	public int getLength()
	{
		return popArray.length;
	}
	
	public Inhabitant get(int i)
	{
		return popArray[i];
	}
	
	/**
	 * 
	 * @return
	 */
	public int GetAvailableID()
	{
		int expected=0;
		for(int i=0; i<popArray.length-1; i++)
		{
			if(popArray[i].ID != expected)
			{
				return expected;
			}
			else
			{
				expected++;
			}
		}
		return expected++;
	}
	
	/**
	 * Removes the dead from the population list.
	 */
	public void BringOutYourDead()
	{
		numDie = 0;
		for (int i=0; i<popArray.length; i++)
		{
			if(!popArray[i].isAlive)
			{
				for(int j=0; j<groups.length; j++)
				{
					groups[j].RemoveMember(popArray[i]);
				}
				numDie ++;
				totalDied++;
				averageLifeExpectancy = averageLifeExpectancy + popArray[i].getAge();
				if (popArray[i].getAge() < youngestDeath )
				{
					youngestDeath = popArray[i].getAge();
					youngID = popArray[i].getID();
				}
				if (popArray[i].getAge() > oldestDeath )
				{
					oldestDeath = popArray[i].getAge();
					oldID = popArray[i].getID();
				}
			}
		}
		if(numDie > 0)
		{
			ReallocatepopArray(popArray.length - numDie);
		}
	}
	
	public void Add(int x, int y, int parent)
	{
		numBorn++;
		totalBorn++;
		ReallocatepopArray(popArray.length+1);
		popArray[popArray.length-1] = new Person(x, y, GetAvailableID(),parent);
		groups[0].AddMember(popArray[popArray.length-1]);
	}
	
	public void KillByID(int id)
	{
		for (int i=0; i<popArray.length; i++)
		{
			if(popArray[i].ID == id)
			{
				popArray[i].Die();
			}
		}
	}
	
	public void AgePopulation()
	{
		Random RNG = new Random();
		for (int i=0; i<popArray.length; i++)
		{
			if (RNG.nextInt((int)((chanceOfDeath-1)/Math.pow(popArray[i].getAge()+1,3)))+1 == 1)
				popArray[i].Die();
			else
				popArray[i].incAge();
		}
	}
	
	public String Report()
	{
		return "\n\n\n----------  YEAR: " + year + "  MONTH: " + (yearStepCounter+1) + " ----------\n"+
				"Last Month -> Born: " + numBorn + "   Died: " + numDie +"   Living: " + popArray.length +"\n"+
				"Total -> Born: " + totalBorn + "  Died: " + totalDied +"\n"+
				"AverageLifeExpectancy: "+averageLifeExpectancy/totalDied + "\n"+
				"Youngest: " + youngestDeath + "(" + youngID + ")   Oldest: "+ oldestDeath +"(" + oldID + ")\n";
	}
	
	/**
	 * adds a new group of nomads with the given founders
	 * @param founders
	 */
	public void AddGroup_Nomad(Inhabitant[] founders)
	{
		ReallocateGroupArray(groups.length+1);
		groups[groups.length-1] = new Nomad(founders);
	}
	
}
